﻿using System.Security.Claims;
using System.Text;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using DoNet.Common.Helpers;
using DoNet.Common.Result;

namespace WechatMp.Web
{
    /// <summary>
    /// 适配多版本密钥，旧版本未过期的可以继续使用
    /// </summary>
    public class TokenProvider
    {
        JwtOption? jwtModel;
        AppService? appService;
        public TokenProvider()
        {
            jwtModel = ConfigHelper.GetSection<JwtOption>("Jwt");
            appService = ServicesHelper.GetService<AppService>();
        }

        /// <summary>
        /// 直接通过appid和加密字符串获取访问令牌接口
        /// </summary>
        /// <param name="appid">用户唯一凭证AppId</param>
        /// <param name="version">密钥secret的版本号</param>
        /// <param name="secret">用户唯一凭证密钥，即appsecret</param>
        /// <returns></returns>
        public TokenResult? GenerateToken(string appid, string version, string secret)
        {
            if (jwtModel != null)
            {
                var keyByteArray = Encoding.UTF8.GetBytes(secret);
                var key = new SymmetricSecurityKey(keyByteArray); //对称秘钥SymmetricSecurityKey
                key.KeyId = version; //密钥key的版本号
                var signingCredentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256Signature);  //签名证书(秘钥，加密算法)SecurityAlgorithms

                var expires = DateTime.UtcNow.Add(TimeSpan.FromMinutes(jwtModel.Expiration));
                var tokenDescripor = new SecurityTokenDescriptor
                {
                    Subject = new ClaimsIdentity(new Claim[] {
                        new Claim(ClaimConst.Audience, appid),
                        new Claim(ClaimConst.Issuer, jwtModel.Issuer),
                        new Claim(ClaimConst.Subject, GrantType.ClientCredentials)
                    }),
                    Expires = expires,
                    SigningCredentials = signingCredentials
                };

                var tokenHandler = new JwtSecurityTokenHandler();
                var token = tokenHandler.CreateToken(tokenDescripor);
                var tokenString = tokenHandler.WriteToken(token);
                TokenResult result = new TokenResult();
                result.AccessToken = tokenString;
                result.ExpiresIn = (int)TimeSpan.FromMinutes(jwtModel.Expiration).TotalMinutes;
                return result;
            }
            return default;
        }

        /// <summary>
        /// 检查用户的Token有效性
        /// </summary>
        /// <param name="token">token令牌</param>
        /// <returns></returns>
        public CommonResult ValidateToken(string token)
        {
            CommonResult result = new CommonResult();
            if (jwtModel != null)
            {
                var tokenHandler = new JwtSecurityTokenHandler();
                try
                {
                    JwtSecurityToken jwtToken = tokenHandler.ReadJwtToken(token);

                    var kid = jwtToken.Header.Kid; // 获取kid头信息 密钥版本id
                    string appid = jwtToken.Claims.ToList()[0].Value; //Audience

                    //这里可以通过缓存获取应用的对应版本的密钥
                    if (appService != null)
                    {
                        //这里可以通过appid获取应用的密钥及版本
                        var app = appService.GetAPP(appid);
                        if (app != null)
                        {
                            //var version = "2"; //
                            if (kid == app.SecretVersion)
                            {
                                //这里需要根据appid获取secret
                                string secret = app.AppSecret;

                                // 验证参数
                                TokenValidationParameters validationParameters = new TokenValidationParameters
                                {
                                    ValidateIssuer = true, // 验证颁发者
                                    ValidIssuer = jwtModel.Issuer, // 令牌的颁发者
                                    ValidateAudience = true, //验证受众
                                    ValidAudience = appid, //令牌的受众
                                    ValidateLifetime = true, //是否检查Token过期时间
                                    ValidateIssuerSigningKey = true, //验证签名密钥
                                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secret)), //用于签名的密钥
                                    ClockSkew = TimeSpan.Zero // 严格过期时间检查 或者可以设置偏差时间
                                };
                                tokenHandler.ValidateToken(token, validationParameters, out SecurityToken validatedToken);
                                if (jwtToken.Subject == GrantType.Password)
                                {
                                    var claimlist = jwtToken?.Payload.Claims as List<Claim>;
                                    result.ResData = claimlist;
                                }
                                result.ErrCode = "0";
                                result.Success = true;
                            }
                            else
                            {
                                //应用密钥已经变更 代表token 已经失效了
                                result.ErrMsg = "验证token异常";
                                result.ErrCode = "401";
                            }
                        }
                    }
                }
                catch (SecurityTokenException ex)
                {
                    if(ex is SecurityTokenExpiredException)
                    {
                        //Log4NetHelper.Error("验证token异常 SecurityTokenExpiredException", ex);
                        result.ErrMsg = "验证token异常";
                        result.ErrCode = "401";
                    }
                    else if(ex is SecurityTokenInvalidLifetimeException)
                    {
                        //Log4NetHelper.Error("验证token异常 SecurityTokenInvalidLifetimeException", ex);
                        result.ErrMsg = "验证token异常";
                        result.ErrCode = "401";
                    }
                }
            }
            return result; 
        }

        /// <summary>
        /// 根据用户获取token
        /// </summary>
        /// <param name="userInfo">用户信息</param>
        /// <param name="appid">应用Id</param>
        /// <returns></returns>
        public TokenResult? LoginToken(UserInfo userInfo, string appid)
        {
            if (jwtModel != null)
            {
                if (appService != null)
                {
                    var app = appService.GetAPP(appid);
                    if (app != null)
                    {
                        string secret = app.AppSecret;
                        var keyByteArray = Encoding.UTF8.GetBytes(secret);
                        var key = new SymmetricSecurityKey(keyByteArray); //对称秘钥SymmetricSecurityKey
                        key.KeyId = app.SecretVersion; //密钥key的版本号
                        var signingCredentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);  //签名证书(秘钥，加密算法)SecurityAlgorithms

                        var authTime = DateTime.UtcNow;//授权时间
                        var expires = authTime.Add(TimeSpan.FromMinutes(jwtModel.Expiration));//过期时间
                        var tokenHandler = new JwtSecurityTokenHandler();
                        var tokenDescripor = new SecurityTokenDescriptor
                        {
                            Subject = new ClaimsIdentity(new Claim[] {
                                new Claim(ClaimConst.Audience, appid),
                                new Claim(ClaimConst.Issuer, jwtModel.Issuer),
                                new Claim(ClaimConst.UserName, userInfo.UserName), 
                                new Claim(ClaimConst.UserId, userInfo.UserId.ToString()), 
                                //new Claim(ClaimConst.Role, _roleService.GetRoleIdsByUserId(userInfo.UserId)),
                                new Claim(ClaimConst.Subject, GrantType.Password)
                            }),
                            Expires = expires,
                            SigningCredentials = signingCredentials
                        };
                        var token = tokenHandler.CreateToken(tokenDescripor);
                        var tokenString = tokenHandler.WriteToken(token);
                        TokenResult result = new TokenResult();
                        result.AccessToken = tokenString;
                        result.ExpiresIn = (int)TimeSpan.FromMinutes(jwtModel.Expiration).TotalMinutes;
                        return result;
                    }
                }
            }
            return default;
        }
    }
}
